package git.soulbgm.security.service;

import git.soulbgm.common.enums.Status;
import git.soulbgm.mapper.*;
import git.soulbgm.pojo.entity.User;
import git.soulbgm.security.pojo.LoginUser;
import git.soulbgm.utils.RequestUtil;
import git.soulbgm.utils.ServletUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

import static git.soulbgm.security.constant.SecurityConstants.*;

/**
 * 自定义用户认证逻辑
 *
 * @author SoulBGM
 * @date 2020-07-30
 */
@Slf4j
@Service("customizeUserDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {

    @Resource
    private UserMapper userMapper;

    @Autowired
    private StringRedisTemplate redis;

    @Autowired
    private PermissionService permissionService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        loginFailureStatistics(username);
        User user = userMapper.selectOne(User.builder().username(username).build());
        if (user == null) {
            log.info("登录用户: {} 不存在", username);
            throw new UsernameNotFoundException("登录用户: " + username + " 不存在");
        } else if (Status.DELETED.code.equals(user.getDelFlag())) {
            log.info("登录用户: {} 已被删除.", username);
            throw new DisabledException("对不起，您的账号: " + username + " 已被删除");
        } else if (Status.DEACTIVATE.code.equals(user.getStatus())) {
            log.info("登录用户: {} 已被停用.", username);
            throw new DisabledException("对不起，您的账号: " + username + " 已停用");
        }
        String ip = RequestUtil.getRemoteIpByServletRequest(ServletUtils.getRequest(), true);
        return new LoginUser(user, permissionService.getMenuPermission(user), ip);
    }

    private void loginFailureStatistics(String username) {
        String loginFailureKey = KEY_PREFIX_LOGIN_FAILURE + username;
        String lockUserKey = KEY_PREFIX_LOGIN_LOCK + username;
        String count = redis.opsForValue().get(loginFailureKey);
        if (count != null && Integer.parseInt(count) >= MAX_LOGIN_FAILURE_COUNT) {
            redis.opsForValue().set(lockUserKey, username, LOGIN_FAILURE_LOCK_DURATION, TimeUnit.SECONDS);
            redis.delete(loginFailureKey);
            throw new LockedException("对不起，您的账号: " + username + " 登录失败超过5次，请一分钟之后重试");
        } else {
            String flag = redis.opsForValue().get(lockUserKey);
            if (flag != null) {
                throw new LockedException("对不起，您的账号: " + username + " 登录失败超过5次，请一分钟之后重试");
            }
        }
    }

}
